package com.dqv5.shorturl.util;

import lombok.extern.slf4j.Slf4j;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.Stack;

/**
 * 进制转换工具,支持 2进制、8进制、16进制、62进制与10进制互转
 *
 * @author duqian
 * @date 2020/6/4
 */
@Slf4j
public class RadixUtil {
    public static final char[] CHARS = {
            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
            'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
            'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
            'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
            'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'
    };

    public static String binToDec(String origin) {
        return new BigInteger(origin, 2).toString();
    }

    public static String decToBin(String origin) {
        return new BigInteger(origin).toString(2);
    }

    public static String octToDec(String origin) {
        return new BigInteger(origin, 8).toString();
    }

    public static String decToOct(String origin) {
        return new BigInteger(origin).toString(8);
    }

    public static String hexToDec(String origin) {
        return new BigInteger(origin, 16).toString();
    }

    public static String decToHex(String origin) {
        return new BigInteger(origin).toString(16);
    }

    /**
     * n进制转为10进制
     *
     * @param origin n进制数
     * @param radix  进制
     * @return
     */
    public static String to10(String origin, int radix) {
        char[] availableChars = Arrays.copyOfRange(CHARS, 0, radix);
        validate(origin, availableChars);
        char[] chars = origin.toCharArray();
        BigInteger result = BigInteger.ZERO;
        for (int i = 0; i < chars.length; i++) {
            int valueIn10 = indexOfPool(chars[chars.length - i - 1], availableChars);
            BigInteger value = new BigInteger(radix + "").pow(i).multiply(new BigInteger(valueIn10 + ""));
            result = result.add(value);
        }
        return result.toString();
    }

    /**
     * 10进制转为n进制
     *
     * @param numberIn10 10进制数
     * @param radix      进制
     * @return
     */
    public static String from10(String numberIn10, int radix) {
        char[] availableChars = Arrays.copyOfRange(CHARS, 0, radix);
        validate(numberIn10, availableChars);
        BigInteger bigInteger = new BigInteger(numberIn10);
        Stack<Character> stack = new Stack<>();
        while (!bigInteger.equals(BigInteger.ZERO)) {
            // 算除法，求商和余数
            BigInteger[] res = bigInteger.divideAndRemainder(new BigInteger(radix + ""));
            stack.push(availableChars[res[1].intValue()]);
            bigInteger = res[0];
        }
        StringBuilder result = new StringBuilder();
        while (!stack.empty()) {
            result.append(stack.pop());
        }
        return result.toString();
    }


    private static void validate(String input, char[] charsPool) {
        char[] chars = input.toCharArray();
        for (char charOfInput : chars) {
            if (indexOfPool(charOfInput, charsPool) == -1) {
                throw new RuntimeException("invalid char:" + charOfInput);
            }
        }
    }

    private static int indexOfPool(char achar, char[] pool) {
        for (int i = 0; i < pool.length; i++) {
            if (achar == pool[i]) {
                return i;
            }
        }
        return -1;
    }

    public static void main(String[] args) {
        String numberIn10 = "120052025685107451";
        String resultIn62 = from10(numberIn10, 62);
        log.info("十进制转为62进制: {}  --> {}", numberIn10, resultIn62);

        String numberIn62 = "8RQ3eA42jV";
        String resultIn10 = to10(numberIn62, 62);
        log.info("62进制转为十进制: {}  --> {}", numberIn62, resultIn10);
    }

}
